package chatclient.netio;

import chatclient.tools.ChatException;
import chatclient.tools.Filter;
import chatclient.tools.QSort;
import java.io.*;
import java.net.URL;
import java.util.Hashtable;
import java.util.Stack;
import java.util.Vector;

public abstract class Message
        implements Cloneable {

    public Message() {
        this((byte) 0);
    }

    public Message(byte byte0) {
        this(byte0, new AttrList());
    }

    public Message(byte byte0, AttrList attrlist) {
        msgid = byte0;
        params = attrlist;
    }

    public Message(InputStream inputstream)
            throws ChatException, IOException {
        this(inputstream, 0x7fffffff);
    }

    public Message(InputStream inputstream, int i)
            throws ChatException, IOException {
        read(inputstream, i);
    }

    public void read(InputStream inputstream)
            throws ChatException, IOException {
        read(inputstream, 0x7fffffff);
    }

    public void read(InputStream inputstream, int i)
            throws ChatException, IOException {
        int j = inputstream.read();
        if (j == -1) {
            throw new EOFException();
        } else {
            msgid = (byte) j;
            params = new AttrList(getAttrProto(), inputstream, i - 1);
            return;
        }
    }

    public void write(OutputStream outputstream)
            throws ChatException, IOException {
        outputstream.write(msgid);
        params.write(outputstream);
    }

    public byte[] getByteCache(boolean flag)
            throws ChatException {
        if (!flag || bytes == null) {
            try {
                ByteArrayOutputStream bytearrayoutputstream = new ByteArrayOutputStream();
                write(bytearrayoutputstream);
                bytes = bytearrayoutputstream.toByteArray();
            } catch (IOException ioexception) {
                throw new ChatException(ioexception.toString());
            }
        }
        return bytes;
    }

    public byte getId() {
        return msgid;
    }

    public AttrList getParams() {
        return params;
    }

    public Message(String as[], Reader reader)
            throws ChatException, IOException {
        char c;
        do {
            c = (char) reader.read();
            if (c == '\n') {
                throw new ChatException("no message name");
            }
        } while (Character.isWhitespace(c));
        String s = new String();
        s = (new StringBuilder(String.valueOf(s))).append(c).toString();
        do {
            reader.mark(1);
            c = (char) reader.read();
            if (!Character.isLetterOrDigit(c)) {
                break;
            }
            s = (new StringBuilder(String.valueOf(s))).append(c).toString();
        } while (true);
        reader.reset();
        for (msgid = 0; (msgid & 0xff) < as.length && !as[msgid & 0xff].equalsIgnoreCase(s); msgid++) {
        }
        if ((msgid & 0xff) == as.length) {
            try {
                msgid = (byte) Integer.parseInt(s);
            } catch (Exception exception) {
                throw new ChatException("unknown message name");
            }
        }
        params = new AttrList(getAttrProto(), reader);
        do {
            c = (char) reader.read();
            if (c == '\n') {
                return;
            }
        } while (Character.isWhitespace(c));
        throw new ChatException((new StringBuilder("invalid character '")).append(c).append("'").toString());
    }

    protected abstract Attr getAttrProto();

    public abstract String getName();

    @Override
    public abstract Object clone();

    public String getName(String as[]) {
        return (getId() & 0xff) >= as.length ? Integer.toString(getId() & 0xff) : as[getId() & 0xff];
    }

    public String toString(String s, String as[]) {
        return (new StringBuilder(String.valueOf(s))).append(getName(as)).append(" ").append(params.toString()).toString();
    }

    private static String expand(Attr attr, int i, Filter afilter[]) {
        StringBuffer stringbuffer = new StringBuffer(attr.getAsString());

        int attrId = attr.getId();

        if (attrId == ClientAttr.NOTE_TEXT || attrId == ClientAttr.CHANNELINFO || attrId == ClientAttr.USERINFO) {
            if ((i & ESC_NOFILTER) == 0 && afilter != null) {
                for (int j = 0; j < afilter.length; j++) {
                    stringbuffer = new StringBuffer(afilter[j].filter(stringbuffer.toString()));
                }

            }
        }

        if ((i & ESC_URL) != 0) {
            for (int k = stringbuffer.length() - 1; k >= 0; k--) {
                char c = stringbuffer.charAt(k);
                if (c >= '\200' || !Character.isLetterOrDigit(c)) {
                    stringbuffer.insert(k + 1, hex[c & 0xf]);
                    stringbuffer.insert(k + 1, hex[(c & 0xf0) / 16]);
                    stringbuffer.setCharAt(k, '%');
                }
            }

        }
        if ((i & ESC_QUOTES) != 0) {
            for (int l = stringbuffer.length() - 1; l >= 0; l--) {
                char c1 = stringbuffer.charAt(l);
                switch (c1) {
                    case 92:
                        stringbuffer.insert(l + 1, '\\');
                        break;

                    case 39:
                        stringbuffer.insert(l + 1, '\'');
                        stringbuffer.setCharAt(l, '\\');
                        break;

                    case 34:
                        stringbuffer.insert(l + 1, '"');
                        stringbuffer.setCharAt(l, '\\');
                        break;
                }
            }

        }
        if ((i & ESC_HTML) != 0) {
            for (int i1 = stringbuffer.length() - 1; i1 >= 0; i1--) {
                char c2 = stringbuffer.charAt(i1);
                switch (c2) {
                    case 38:
                        stringbuffer.insert(i1 + 1, "amp;");
                        break;

                    case 60:
                        stringbuffer.insert(i1 + 1, "lt;");
                        stringbuffer.setCharAt(i1, '&');
                        break;

                    case 39:
                        stringbuffer.insert(i1 + 1, "#39;");
                        stringbuffer.setCharAt(i1, '&');
                        break;

                    case 34:
                        stringbuffer.insert(i1 + 1, "quot;");
                        stringbuffer.setCharAt(i1, '&');
                        break;

                    case 92:
                        stringbuffer.insert(i1 + 1, "#92;");
                        stringbuffer.setCharAt(i1, '&');
                        break;
                }
            }

        }
        return stringbuffer.toString();
    }

    public void expand(String s, StringBuffer astringbuffer[], int i, Filter afilter[]) {
        expand(s, params, astringbuffer, i, afilter);
    }

    public static void expand(String s, AttrList attrlist, StringBuffer astringbuffer[], int i, Filter afilter[]) {
        if (s == null) {
            return;
        }
        Attr aattr[] = (Attr[]) attrlist.toArray();
        int j = 0;
        int k = s.length();
        char ac[] = new char[k];
        s.getChars(0, k, ac, 0);
        int l = 0;
        int i1 = 0;
        char ac1[] = new char[10];
        ac1[0] = '\0';
        boolean aflag[] = new boolean[10];
        aflag[0] = false;
        int ai[] = new int[10];
        ai[0] = 0;
        int ai1[] = new int[10];
        ai1[0] = aattr.length;
        int ai2[] = new int[10];
        Vector avector[] = new Vector[10];
        int ai3[] = new int[10];
        Vector avector1[] = new Vector[10];
        Hashtable ahashtable[] = new Hashtable[10];
        try {
            while (j < k) {
                boolean flag = false;
                int j1 = 0;
                char c;
                label0:
                switch (c = ac[j++]) {
                    case 37:
                        byte byte0;
                        for (byte0 = 0; j < k && Character.isDigit(c = ac[j++]); byte0 = (byte) (10 * byte0 + (c - 48))) {
                        }
                        if (c == '.') {
                            flag = true;
                            for (j1 = 0; j < k && Character.isDigit(c = ac[j++]); j1 = 10 * j1 + (c - 48)) {
                            }
                        }
                        switch (ac1[i1 + 1] = c) {
                            case 37:
                            case 58:
                                int k1 = i;
                                if (c == ':') {
                                    k1 = ac[j++] - 48;
                                    j++;
                                }
                                for (int i3 = ai[i1]; i3 < ai1[i1]; i3++) {
                                    if (aattr[i3].getId() == byte0 && (!flag || j1-- == 0)) {
                                        if (!aflag[i1]) {
                                            System.out.println("ADZ - Message.java->expand:aattr["+i3+"]="+aattr[i3]+":AttrId="+aattr[i3].getValue());
                                            if (aattr[i3].getId() == ClientAttr.NOTE_TEXT || aattr[i3].getId() == ClientAttr.USERINFO || aattr[i3].getId() == ClientAttr.CHANNELINFO)
                                            {
                                                String [] tabAattr = aattr[i3].getAsString().split(" ");
                                                for (int ii = 0; ii < tabAattr.length; ii++)
                                                {
                                                    if (!tabAattr[ii].equals(""))
                                                    {
                                                        System.out.println("ADZ - Message.java->expand:tabAattr["+ii+"]="+tabAattr[ii]);
                                                        Attr at = new ClientAttr(ClientAttr.NOTE_TEXT, tabAattr[ii]);
                                                        if (at.getAsString().toLowerCase().startsWith("http:/"))
                                                        {
                                                            if (ii == 0)
                                                                astringbuffer[l].append(expand(at, k1, new Filter[0]));
                                                            else
                                                                astringbuffer[l].append(" " + expand(at, k1, new Filter[0]));
                                                        }
                                                        else
                                                        {
                                                            if (ii == 0)
                                                                astringbuffer[l].append(expand(at, k1, afilter));
                                                            else
                                                                astringbuffer[l].append(" " + expand(at, k1, afilter));
                                                        }
                                                    }
                                                }
                                            }
                                            else
                                                astringbuffer[l].append(expand(aattr[i3], k1, afilter));
                                        }
                                        break label0;
                                    }
                                }

                                break label0;

                            case 61:
                                StringBuilder stringbuffer = new StringBuilder();
                                while (j < k && (c = ac[j++]) != '?') {
                                    stringbuffer.append(c);
                                }
                                ai[i1 + 1] = ai[i1];
                                ai1[i1 + 1] = ai1[i1];
                                aflag[i1 + 1] = true;
                                for (int j3 = ai[i1]; j3 < ai1[i1]; j3++) {
                                    if (aattr[j3].getId() == byte0 && (!flag || j1-- == 0) && expand(aattr[j3], i, afilter).equals(stringbuffer.toString())) {
                                        aflag[i1 + 1] = aflag[i1];
                                    }
                                }

                                i1++;
                                break label0;

                            case 126:
                                StringBuilder stringbuffer1 = new StringBuilder();
                                while (j < k && (c = ac[j++]) != '?') {
                                    stringbuffer1.append(c);
                                }
                                ai[i1 + 1] = ai[i1];
                                ai1[i1 + 1] = ai1[i1];
                                aflag[i1 + 1] = true;
                                for (int k3 = ai[i1]; k3 < ai1[i1]; k3++) {
                                    if (aattr[k3].getId() == byte0 && (!flag || j1-- == 0) && expand(aattr[k3], i, afilter).indexOf(stringbuffer1.toString()) >= 0) {
                                        aflag[i1 + 1] = aflag[i1];
                                    }
                                }

                                i1++;
                                break label0;

                            case 43:
                                ai[i1 + 1] = ai[i1];
                                ai1[i1 + 1] = ai1[i1];
                                aflag[i1 + 1] = true;
                                for (int l1 = ai[i1]; l1 < ai1[i1]; l1++) {
                                    if (aattr[l1].getId() != byte0 || flag && j1-- != 0) {
                                        continue;
                                    }
                                    aflag[i1 + 1] = aflag[i1];
                                    break;
                                }

                                i1++;
                                break label0;

                            case 63:
                                ai[i1 + 1] = ai[i1];
                                ai1[i1 + 1] = ai1[i1];
                                aflag[i1 + 1] = true;
                                for (int i2 = ai[i1]; i2 < ai1[i1]; i2++) {
                                    if (aattr[i2].getId() == byte0 && (!flag || j1-- == 0) && expand(aattr[i2], i, afilter).length() > 0) {
                                        aflag[i1 + 1] = aflag[i1];
                                    }
                                }

                                i1++;
                                break label0;

                            case 35:
                                int j2 = 0;
                                for (int l3 = ai[i1]; l3 < ai1[i1]; l3++) {
                                    if (aattr[l3].getId() == byte0 && (!flag || j1-- == 0)) {
                                        j2++;
                                    }
                                }

                                if (!aflag[i1]) {
                                    astringbuffer[l].append(j2);
                                }
                                break label0;

                            case 91:
                            case 123:
                                avector[i1] = new Vector();
                                avector1[i1] = new Vector();
                                ahashtable[i1] = new Hashtable();
                                int k2;
                                for (k2 = ai[i1]; k2 < ai1[i1]; k2++) {
                                    if (aattr[k2].getId() == byte0 && (!flag || j1-- == 0)) {
                                        String s2 = expand(aattr[k2], 0, afilter).toLowerCase();
                                        avector[i1].addElement(s2);
                                        avector1[i1].addElement(new Integer(k2));
                                        ahashtable[i1].put(s2, new Integer(avector1[i1].size() - 1));
                                    }
                                }

                                avector1[i1].addElement(new Integer(k2));
                                if (c == '{') {
                                    (new QSort()).sort(avector[i1]);
                                }
                                ai3[i1] = 0;
                                ai2[i1] = j;
                                if (avector[i1].isEmpty()) {
                                    ai[i1 + 1] = 0;
                                    ai1[i1 + 1] = 0;
                                    aflag[i1 + 1] = true;
                                } else {
                                    String s3 = (String) avector[i1].elementAt(0);
                                    int l2 = ((Integer) ahashtable[i1].get(s3)).intValue();
                                    ai[i1 + 1] = ((Integer) avector1[i1].elementAt(l2)).intValue();
                                    ai1[i1 + 1] = ((Integer) avector1[i1].elementAt(l2 + 1)).intValue();
                                    aflag[i1 + 1] = aflag[i1];
                                }
                                i1++;
                                break label0;
                        }
                        if (!aflag[i1]) {
                            astringbuffer[l].append(c);
                        }
                        break;

                    case 33:
                        aflag[i1] = !aflag[i1] || aflag[i1 - 1];
                        break;

                    case 63:
                        switch (ac1[i1]) {
                            default:
                                break label0;

                            case 43:
                            case 61:
                            case 63:
                            case 126:
                                i1--;
                                break label0;

                            case 91:
                            case 123:
                                break;
                        }
                        if (!aflag[i1]) {
                            aflag[i1] = ai3[i1 - 1] + 1 >= avector[i1 - 1].size() || aflag[i1 - 1];
                        }
                        break;

                    case 93:
                    case 125:
                        i1--;
                        if (++ai3[i1] < avector[i1].size()) {
                            String s1 = (String) avector[i1].elementAt(ai3[i1]);
                            int i4 = ((Integer) ahashtable[i1].get(s1)).intValue();
                            ai[i1 + 1] = ((Integer) avector1[i1].elementAt(i4)).intValue();
                            ai1[i1 + 1] = ((Integer) avector1[i1].elementAt(i4 + 1)).intValue();
                            aflag[i1 + 1] = aflag[i1];
                            j = ai2[i1];
                            i1++;
                        }
                        break;

                    case 92:
                        if (j >= k) {
                            if (!aflag[i1]) {
                                astringbuffer[l].append(c);
                            }
                            break;
                        }
                        switch (c = ac[j++]) {
                            case 110:
                                if (!aflag[i1]) {
                                    astringbuffer[l].append('\n');
                                }
                                break label0;

                            case 101:
                                if (!aflag[i1]) {
                                    astringbuffer[l].append('\033');
                                }
                                break label0;
                        }
                        if (!aflag[i1]) {
                            astringbuffer[l].append(c);
                        }
                        break;

                    case 124:
                        if (!aflag[i1] && l + 1 < astringbuffer.length) {
                            l++;
                            break;
                        }

                    default:
                        if (!aflag[i1]) {
                            astringbuffer[l].append(c);
                        }
                        break;
                }
            }
        } catch (Exception exception) {
            exception.printStackTrace();
        }
    }

    public static void convertToHtml(StringBuffer stringbuffer, String s, URL url) {

        Stack<String> openTags = new Stack<String>();
        
        for (int k = 0; k < s.length(); k++) {

            char c = s.charAt(k);

            switch (c) {
                case ESC:
                {
                    c = s.charAt(++k);
                    k++;

                    switch (c) {

                        // case F/B/I/P/S/C/G start
                        case 'F':
                        case 'B':
                        case 'I':
                        case 'P':
                        case 'S':
                        case 'C':
                        case 'G': {
                            int i = k;
                            for (; s.charAt(k) != ';'; k++) {
                            }

                            switch (c) {

                                case 'F':
                                    stringbuffer.append("<font face=").append(s.substring(i, k));
                                    openTags.push("font");
                                    break;

                                case 'B':
                                    stringbuffer.append("<b>");
                                    openTags.push("b");
                                    break;

                                case 'I':
                                    stringbuffer.append("<i>");
                                    openTags.push("i");
                                    break;

                                case 'P':
                                    stringbuffer.append("<p>");
                                    openTags.push("p");
                                    break;

                                case 'S':
                                    stringbuffer.append("<div class=").append('"');
                                    if (s.substring(k - 2, k - 1).equals("+")) {
                                        stringbuffer.append("size").append(s.substring(k - 1, k));
                                    } else if (s.substring(k - 2, k - 1).equals("-")) {
                                        stringbuffer.append("sizeM").append(s.substring(k - 1, k));
                                    }
                                    stringbuffer.append('"').append(">");
                                    openTags.push("div");
                                    break;

                                case 'C':
                                    stringbuffer.append("<font color=").append('"');
                                    stringbuffer.append(s.substring(i, k)).append('"').append(">");
                                    openTags.push("font");
                                    break;

                                case 'G':
                                    stringbuffer.append("\n").append("<img alt=\"\" src=\"");
                                    try {
                                        stringbuffer.append((new URL(url, (new StringBuilder(String.valueOf(s.substring(i, k)))).append(".gif").toString())).toString());
                                    } catch (Exception exception) {
                                        exception.printStackTrace();
                                    }
                                    stringbuffer.append("\">");
                                    break;

                                default:
                                    break;
                            }
                        }
                        break;
                        // case F/B/I/P/S/C/G end


                        case 'c':
                            if(!openTags.empty() && openTags.peek().equals("font"))
                            {
                              stringbuffer.append("</font>");
                              openTags.pop();
                            }
                            break;

                        case 'g':
                            break;

                        case 'f':
                            if(!openTags.empty() && openTags.peek().equals("font"))
                            {
                              stringbuffer.append("</font>");
                              openTags.pop();
                            }
                            break;

                        case 's':
                            if(!openTags.empty() && openTags.peek().equals("div"))
                            {
                              stringbuffer.append("</div>");
                              openTags.pop();
                            }
                            break;

                        case 'b':
                            if(!openTags.empty() && openTags.peek().equals("b"))
                            {
                              stringbuffer.append("</b>");
                              openTags.pop();
                            }
                            break;

                        case 'i':
                            if(!openTags.empty() && openTags.peek().equals("i"))
                            {
                              stringbuffer.append("</i>");
                              openTags.pop();
                            }
                            break;

                        case 'p':
                            if(!openTags.empty() && openTags.peek().equals("p"))
                            {
                              stringbuffer.append("</p>");
                              openTags.pop();
                            }
                            break;

                        default:
                            break;

                    }
                    break;
                }

                case 10: // new line
                    stringbuffer.append("<br>");
                    int j = k + 1;
                    break;

                default:
                    if (c == '<') {
                        stringbuffer.append("&lt;");
                    } else if (c == '>') {
                        stringbuffer.append("&gt;");
                    } else if (c == '&') {
                        stringbuffer.append("&amp;");
                    } else {
                        stringbuffer.append(c);
                    }
                    break;
            }
        }
        
        while(!openTags.empty())
        {
             stringbuffer.append("</" + openTags.pop() + ">");
        }   
    }
    
    
    public static byte ESC_NONE = 0;
    public static byte ESC_HTML = 1;
    public static byte ESC_QUOTES = 2;
    public static byte ESC_URL = 4;
    public static byte ESC_NOFILTER = 8;
    static char hex[] = {
        '0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
        'a', 'b', 'c', 'd', 'e', 'f'
    };
    private byte msgid;
    private AttrList params;
    private byte bytes[];
    private static final char ESC = 27;
    public static final int MAXNEST = 10;
}
